package cc.magicjson.design.copy;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class DeepCopyUtil {

    public static <T> T deepCopy(T object) throws Exception {
        if (object == null) {
            return null;
        }
        
        Class<?> clazz = object.getClass();
        
        // 处理基本类型、String和不可变类型
        if (clazz.isPrimitive() || object instanceof String || object instanceof Number || 
            object instanceof Boolean || object instanceof Character) {
            return object;
        }
        
        // 处理数组
        if (clazz.isArray()) {
            return (T) deepCopyArray(object);
        }
        
        // 处理集合
        if (object instanceof Collection) {
            return (T) deepCopyCollection((Collection<?>) object);
        }
        
        // 处理Map
        if (object instanceof Map) {
            return (T) deepCopyMap((Map<?, ?>) object);
        }
        
        // 处理普通对象
        return deepCopyObject(object);
    }

    private static Object deepCopyArray(Object array) throws Exception {
        int length = Array.getLength(array);
        Object newArray = Array.newInstance(array.getClass().getComponentType(), length);
        for (int i = 0; i < length; i++) {
            Array.set(newArray, i, deepCopy(Array.get(array, i)));
        }
        return newArray;
    }

    private static Collection<?> deepCopyCollection(Collection<?> collection) throws Exception {
        Collection<Object> newCollection = collection.getClass().getDeclaredConstructor().newInstance();
        for (Object item : collection) {
            newCollection.add(deepCopy(item));
        }
        return newCollection;
    }

    private static Map<?, ?> deepCopyMap(Map<?, ?> map) throws Exception {
        Map<Object, Object> newMap = map.getClass().getDeclaredConstructor().newInstance();
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            newMap.put(deepCopy(entry.getKey()), deepCopy(entry.getValue()));
        }
        return newMap;
    }

    private static <T> T deepCopyObject(T object) throws Exception {
        Class<?> clazz = object.getClass();
        T copy = (T) clazz.getDeclaredConstructor().newInstance();

        // 获取所有字段，包括私有字段
        List<Field> fields = new ArrayList<>();
        while (clazz != null) {
            fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
            clazz = clazz.getSuperclass();
        }

        for (Field field : fields) {
            if (!Modifier.isStatic(field.getModifiers())) {
                field.setAccessible(true);
                Object value = field.get(object);
                if (value != null) {
                    field.set(copy, deepCopy(value));
                }
            }
        }

        return copy;
    }

    // 使用示例
    public static void main(String[] args) throws Exception {
        // 创建一个复杂对象进行测试
        List<Map<String, Object>> complexObject = new ArrayList<>();
        Map<String, Object> item = new HashMap<>();
        item.put("name", "John");
        item.put("age", 30);
        item.put("address", new Address("123 Main St", "City", "12345"));
        complexObject.add(item);

        // 进行深拷贝
        List<Map<String, Object>> deepCopy = DeepCopyUtil.deepCopy(complexObject);

        // 修改原始对象
        ((Address) complexObject.get(0).get("address")).setStreet("456 Elm St");

        // 打印结果
        System.out.println("Original: " + complexObject);
        System.out.println("Deep Copy: " + deepCopy);
    }
}

class Address {
    private String street;
    private String city;
    private String zipCode;


    public Address() {
    }
    public Address(String street, String city, String zipCode) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
    }

    // Getters and setters
    public void setStreet(String street) {
        this.street = street;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                ", zipCode='" + zipCode + '\'' +
                '}';
    }
}